package org.duang.kit;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Type;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.net.URISyntaxException;
import java.nio.charset.Charset;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.codec.DecoderException;
import org.apache.commons.codec.binary.Hex;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringEscapeUtils;
import org.bson.types.ObjectId;
import org.duang.common.dto.HeadDto;
import org.duang.common.dto.ReturnDto;
import org.duang.common.enums.IEnums;
import org.duang.config.Config;
import org.duang.config.InstanceFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializeConfig;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.alibaba.fastjson.serializer.SimpleDateFormatSerializer;
import com.aliyun4j.utils.Tools;

public class ToolsKit {

	private static Logger logger = LoggerFactory.getLogger(ToolsKit.class);
	public static final String HASH_ALGORITHM = "SHA-1";
	public static final int HASH_INTERATIONS = 1024;
	private static final int SALT_SIZE = 8;
	private static String WEB_ROOT_PATH = "";
	public static final String CHARSET_UTF_8 = "utf-8";

	private static SerializeConfig jsonConfig = new SerializeConfig();
	// 定义一个IdEntity安全线程类
	private static DuangThreadLocal<String> objectIdThreadLocal = new DuangThreadLocal<String>() {
		public String initialValue() {
			return "";
		}
	};
	
	static {
		jsonConfig.put(Date.class, new SimpleDateFormatSerializer("yyyy-MM-dd HH:mm:ss"));
		// jsonConfig.put(Charset.class, new CharArraySerializer());
	}

	/***
	 * 判断传入的对象是否为空
	 * 
	 * @param obj
	 *            待检查的对象
	 * @return 返回的布尔值,为空或等于0时返回true
	 */
	public static boolean isEmpty(Object obj) {
		return checkObjectIsEmpty(obj, true);
	}

	/***
	 * 判断传入的对象是否不为空
	 * 
	 * @param obj
	 *            待检查的对象
	 * @return 返回的布尔值,不为空或不等于0时返回true
	 */
	public static boolean isNotEmpty(Object obj) {
		return checkObjectIsEmpty(obj, false);
	}

	private static boolean checkObjectIsEmpty(Object obj, boolean bool) {
		if (null == obj)
			return bool;
		else if (obj == "" || "".equals(obj))
			return bool;
		else if (obj instanceof Integer || obj instanceof Long || obj instanceof Double) {
			try {
				Double.parseDouble(obj + "");
			} catch (Exception e) {
				return bool;
			}
		} else if (obj instanceof String) {
			if (((String) obj).length() <= 0)
				return bool;
			if ("null".equalsIgnoreCase(obj+""))
				return bool;
		} else if (obj instanceof Map) {
			if (((Map) obj).size() == 0)
				return bool;
		} else if (obj instanceof Collection) {
			if (((Collection) obj).size() == 0)
				return bool;
		} else if (obj instanceof Object[]) {
			if (((Object[]) obj).length == 0)
				return bool;
		}
		return !bool;
	}

	public static String builderObjectId() {
		return new ObjectId().toString();
	}

	public static void setObjectId2Thread(String objectId) {
		objectIdThreadLocal.set(objectId);
	}

	public static String getObjectIdByThread() {
		return objectIdThreadLocal.get();
	}

	public static void removeObjectIdByThread() {
		objectIdThreadLocal.remove();
	}
	
	public static ObjectId message2ObjectId(String id) {
		boolean isObjectId = ToolsKit.isValidObjectId(id);
		if (isObjectId) {
			return new ObjectId(id);
		} else {
			throw new IllegalArgumentException(id + " is not Vaild ObjectId");
		}
	}

	/**
	 * 验证是否为MongoDB 的ObjectId
	 * 
	 * @param str
	 *            待验证字符串
	 * @return  如果是则返回true
	 */
	public static boolean isValidObjectId(String str) {
		if (ToolsKit.isEmpty(str)) {
			return false;
		}
		int len = str.length();
		if (len != 24) {
			return false;
		}
		for (int i = 0; i < len; i++) {
			char c = str.charAt(i);
			if ((c < '0') || (c > '9')) {
				if ((c < 'a') || (c > 'f')) {
					if ((c < 'A') || (c > 'F')) {
						logger.warn(str + " is not ObjectId!!");
						return false;
					}
				}
			}
		}
		return true;
	}

	/**
	 * 关键字是否存在于map中, 如果存在返回true, 不存在返回false
	 * 
	 * @param key
	 * @param map
	 * @return
	 */
	public static boolean isExist(String key, Map map) {
		if (map.containsKey(key)) {
			logger.debug("the " + key + " exist on map, can't repeat add...");
			return true;
		}
		return false;
	}

	public static boolean isExist(String key, java.util.Collection<?> coll) {
		if (coll.contains(key)) {
			logger.debug("the " + key + " exist on set, can't repeat add...");
			return true;
		}
		return false;
	}

	/**
	 * 去空格, 变小写
	 * 
	 * @param str
	 *            字符串
	 * @return
	 */
	public static String toLowerCase(String str) {
		if (null == str)
			return "";
		return str.trim().toLowerCase();
	}

	/**
	 * Hex编码.
	 */
	public static String encodeHex(byte[] input) {
		return new String(Hex.encodeHex(input)); // .encodeHexString(input);
	}

	/**
	 * Hex解码.
	 */
	public static byte[] decodeHex(String input) {
		try {
			return Hex.decodeHex(input.toCharArray());
		} catch (DecoderException e) {
			throw new RuntimeException(e);
		}
	}

	/**
	 * 根据codeid设定安全密码的Salt
	 */
	public static byte[] buildEntryptSalt(int codeid) {
		return Digests.generateSalt(SALT_SIZE);
	}

	/**
	 * 随机设定安全密码的Salt
	 */
	public static byte[] buildEntryptSalt() {
		return Digests.generateSalt(SALT_SIZE);
	}

	/**
	 * 设定安全的密码，生成随机的salt并经过1024次 sha-1 hash
	 */
	public static String buildEntryptPassword(String password, byte[] salt) {
		byte[] hashPassword = Digests.sha1(password.getBytes(), salt, HASH_INTERATIONS);
		return Encodes.encodeHex(hashPassword);
	}

	/**
	 * 去掉不符合指定字符串里包含的字符
	 * @param password
	 * @return
	 */
	private static String vaildPassword(String password){
    	char[] charArray = password.toCharArray();
    	StringBuilder sb = new StringBuilder();
    	for(int i=0; i<charArray.length; i++){    		
	    	for(int j=0; j<Encodes.BASE62.length; j++){
	    		if(charArray[i] == Encodes.BASE62[j]){
	    			sb.append(charArray[i]);
	    		}
	    	}
    	}
    	if(sb.length() > 0){
    		password = sb.toString();
    	}
    	return password;
    }
	
	/**
	 * 取根路径
	 * 
	 * @return
	 */
	public static String getWebRootPath() {
		if (ToolsKit.isEmpty(WEB_ROOT_PATH)) {
			try {
				WEB_ROOT_PATH = new File(ToolsKit.class.getResource("/").toURI().getPath()).getParentFile()
						.getParentFile().getCanonicalPath();
			} catch (URISyntaxException e) {
				e.printStackTrace();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		return WEB_ROOT_PATH;
	}

	public static String getCurrentDate(String format) {
		return formatDate(new Date(), format);
	}

	public static String formatDate(Date date) {
		return formatDate(date, Config.getDefaultDateFormat());
	}

	public static String formatDate(Date date, String format) {
		try {
			java.text.SimpleDateFormat sdf = new java.text.SimpleDateFormat(format);
			return sdf.format(date);
		} catch (Exception ex) {
			ex.printStackTrace();
			return "";
		}
	}

	public static Date parseDate(String stringDate) {
		return parseDate(stringDate, Config.getDefaultDateFormat());
	}

	public static Date parseDate(String stringDate, String format) {
		java.text.SimpleDateFormat sdf = new java.text.SimpleDateFormat(format);
		try {
			return sdf.parse(stringDate);
		} catch (ParseException e) {
			e.printStackTrace();
			return null;
		}
	}

	/**
	 * 是否安全URL
	 * 
	 * @param url
	 * @return
	 */
	public static boolean isSecurityUrl(String url) {
		if (url.indexOf(".") > -1 || url.indexOf("#") > -1 || url.indexOf(";") > -1) {
			return false;
		}
		return (url.equals("/"))  ? true : InstanceFactory.getActionMapping().containsKey(url);
	}

	public final static Map<String, String> HTML_CHAR = new HashMap<String, String>();

	static {
		HTML_CHAR.put("&", "&#38;");
		HTML_CHAR.put("\"", "&#34;");
		HTML_CHAR.put("<", "&#60;");
		HTML_CHAR.put(">", "&#62;");
		HTML_CHAR.put("'", "&#39;");
	}

	public static final Object getSafeValue(Object obj) {
		try {
			return toHTMLChar(String.valueOf(obj));
		} catch (Exception e) {
			e.printStackTrace();
			return obj;
		}
	}

	/**
	 * HTML字符转换表
	 */
	public static final String toHTMLChar(String str) {
		
		return StringEscapeUtils.escapeHtml(str);
		/*
		if (isEmpty(str)) {
			return "";
		}
		StringBuilder sb = new StringBuilder(str);
		char tempChar;
		String tempStr;
		for (int i = 0; i < sb.length(); i++) {
			tempChar = sb.charAt(i);
			if (HTML_CHAR.containsKey(Character.toString(tempChar))) {
				tempStr = (String) HTML_CHAR.get(Character.toString(tempChar));
				sb.replace(i, i + 1, tempStr);
				i += tempStr.length() - 1;
			}
		}
		return sb.toString();
		*/
	}
	
	public static final String htmlChar2String(String htmlChar) {
		return StringEscapeUtils.unescapeHtml(htmlChar);
		/*
		if(isEmpty(htmlChar)) return "";
		for(Iterator<Entry<String,String>> it = HTML_CHAR.entrySet().iterator(); it.hasNext();){
			Entry<String,String> entry = it.next();
			htmlChar = htmlChar.replace(entry.getValue(), entry.getKey());
		}
		return htmlChar;
		*/
	}

	public static void printStackTrace() {
		StackTraceElement[] stes = new Exception().getStackTrace();
		for (StackTraceElement ste : stes) {
			System.out.println(ste.getClassName() + " ----- " + ste.getMethodName());
		}
		System.out.println("=======================================");
	}

	public static String toJsonString(Object obj) {
		return JSON.toJSONString(obj, jsonConfig, SerializerFeature.DisableCircularReferenceDetect);
	}

	public static <T> T jsonParseObject(String jsonText, Class<T> clazz) {
		return JSON.parseObject(jsonText, clazz);
	}
	
	public static <T> T jsonParseObject(String jsonText, Type typeClazz) {
		//typeClazz = new TypeReference<EaseMobMessages<TextMessage>>(){}
		return JSON.parseObject(jsonText, typeClazz);
	}

	public static <T> List<T> jsonParseArray(String jsonText, Class<T> clazz) {
		return JSON.parseArray(jsonText, clazz);
	}
	
	public static InputStream string2InputStream(String str, String encoding) {
		try {
			return new ByteArrayInputStream(str.getBytes(encoding));
		} catch (UnsupportedEncodingException e) {
			logger.warn("ToolsKit str2InputStream fail: " + e.getMessage());
			return null;
		}
	}
	
	public static String inputStream2String(InputStream is) {
		try {
			return IOUtils.toString(is, toCharset(CHARSET_UTF_8));
		} catch (IOException e) {			
			logger.warn("ToolsKit inputStream2String fail: " + e.getMessage());
			return null;
		}
	}

	public static Charset toCharset(String charset) {
        return charset == null ? Charset.defaultCharset() : Charset.forName(charset);
    }
	
	public static String inputStream2String(InputStream is, String encoding) {
		try {
			return IOUtils.toString(is, toCharset(encoding));
		} catch (IOException e) {			
			logger.warn("ToolsKit inputStream2String fail: " + e.getMessage());
			return null;
		}
		/*
		ByteArrayOutputStream baos = new ByteArrayOutputStream();
		int i = -1;
		String result = "";
		try {
			while ((i = is.read()) != -1) {
				baos.write(i);
			}
			result = baos.toString();
		} catch (IOException e) {
			logger.warn("ToolsKit inputStream2String fail: " + e.getMessage());
		} finally {
			try {
				baos.close();
			} catch (IOException e) {
			}
		}		
		return result;
		*/
	}
	
	public static boolean validateDuangRequest(String authStr, String userAgent) {
		String uuid = userAgent.substring(userAgent.lastIndexOf("/")+1, userAgent.length());
		int index = Integer.parseInt(Character.toString(uuid.charAt(uuid.length())));
		return false;
	}

	public static void main(String[] args) {
		String htmlChar= "&#60;p&#62;&#60;img alt=&#34;&#34; src=&#34;http://file.sythealth.com/img/syt-community/feed/20160109/ac9e710d659542dbaae17c921a74f675.jpg&#34; /&#62;&#60;/p&#62;";
		String str = ToolsKit.htmlChar2String(htmlChar);
		System.out.println(str);
	}
	
	public static ReturnDto<Object> buildReturnDto(IEnums enums, Object obj) {
		ReturnDto<Object> dto = new ReturnDto<Object>();
		HeadDto head = new HeadDto();
		if (ToolsKit.isEmpty(enums)) {
			head.setRet(IEnums.IENUMS_SUCCESS_CODE);
			head.setMsg(IEnums.IENUMS_SUCCESS_MESSAGE);
		} else {
			head.setRet(enums.getCode());
			head.setMsg(enums.getMessage());
		}
		dto.setHead(head);
		dto.setData(obj);
		return dto;
	}
	
	/**
	 * 中文转换为全拼
	 * @param chineseString
	 * @return
	 */
	public static String converterToPinYin(String chineseString) {
		return ConvertToPinYin.converterToPinYin(chineseString);
	}
	
	
	public static String getStackTraceAsString(Exception e) {
		StringWriter stringWriter = new StringWriter();
		e.printStackTrace(new PrintWriter(stringWriter));
		return stringWriter.toString();
	}
	
	public static Exception builderExceptionStackTrace(Exception e) {		
		String str = getStackTraceAsString(e);
		String[] array  = str.split("\tat ");
//		java.util.LinkedHashSet<String> set = new java.util.LinkedHashSet<String>();
//		java.util.Arrays.sort(array, java.util.Collections.reverseOrder());		
//		set.addAll(java.util.Arrays.asList(array));
//		StringBuilder sb = new StringBuilder();
		StringBuilder sb = new StringBuilder();
		List<StackTraceElement> stackTraces = new ArrayList<StackTraceElement>();
		for(String exString : array){
			int index = exString.indexOf("Caused by");
			if(index > -1){
				sb.delete(0, sb.length());
				sb.append(exString.substring(index+10));
			}
			if(exString.indexOf(Config.getBasePackagePath()) > -1 && exString.indexOf("$$") == -1 && exString.indexOf("invoke") == -1){
				stackTraces.add(builderStackTraceElement(exString));
//				sb.append(exString).append("\tat ");
			}
		}
		String declaringClass = sb.substring(0, sb.indexOf(":"));		
		String message = sb.substring(sb.indexOf(":")+1, sb.length()).replace("\r\n", "").trim();
		Exception ex = (Exception)ObjectKit.newInstance(declaringClass.trim(), message, String.class);
		java.util.Collections.reverse(stackTraces);
		ex.setStackTrace(stackTraces.toArray(new StackTraceElement[]{}));
//		System.out.println("##########: " + getStackTraceAsString(ex));
		return ex;
	}
	
	private static StackTraceElement builderStackTraceElement(String exceptionStr) {
		String[] exceArray = exceptionStr.split("\\(");
		String tmpStr0 = exceArray[0];
		String tmpStr1 = exceArray[1];
//		System.out.println(tmpStr1.indexOf(")"));
		int tmpInt0 = 0;
		int tmpInt1 = 0;
		if(ToolsKit.isNotEmpty(tmpStr0) && ToolsKit.isNotEmpty(tmpStr1)){
			tmpInt0 = tmpStr0.lastIndexOf(".");
			tmpInt1 = tmpStr1.indexOf(":");	
			String declaringClass = tmpStr0.substring(0, tmpInt0);
			String monthodName = tmpStr0.substring(tmpInt0+1);
			String fileName = tmpStr1.substring(0, tmpInt1);		
			int lineNumber =  Integer.parseInt(tmpStr1.substring(tmpInt1+1, tmpStr1.indexOf(")")));
//			System.out.println(declaringClass+"            "+monthodName+"            "+fileName+"            "+lineNumber);
			return  new StackTraceElement(declaringClass, monthodName, fileName, lineNumber);
		} 
		return null;
	}
	
	public static String getLocalHostIP() {
		return getLocalHostIP(true);
	}
	
	/**
	 * 取本机IP地址
	 * @param isPublicIp	是否取公网IP  为true时取公网IP，为false时取私网IP
	 * @return
	 */
	  public static String getLocalHostIP(boolean isPublicIp) {
	       try {
//	           InetAddress addr = InetAddress.getLocalHost();
//	           String ipAddress =  addr.getHostAddress();
//	           if (ipAddress.equals("127.0.0.1") && ipAddress.startsWith("10")) {
//	        	   ipAddress = getLocalMachineIp();
//	           }   
//	           return ipAddress;
	           return getLocalMachineIp(isPublicIp);
	       } catch (Exception e) {
	           e.printStackTrace();
	           return "";
	       }
	   }
	  
	  /**
	   * 阿里云的ECS内网IP都是以10开头
	   * @param isPublicIp
	   * @return
	   */
	  private static String getLocalMachineIp(boolean isPublicIp) {
			InetAddressValidator validator = new InetAddressValidator();
			String candidate = new String();
			try {
				Enumeration<NetworkInterface> ifaces = NetworkInterface.getNetworkInterfaces();
				while (ifaces.hasMoreElements()) {
					NetworkInterface iface = (NetworkInterface) ifaces.nextElement();
					if (iface.isUp()) {
						Enumeration<InetAddress> addresses = iface.getInetAddresses();
						while (addresses.hasMoreElements()) {
							InetAddress address = (InetAddress) addresses.nextElement();
							if ((!address.isLinkLocalAddress()) && (address.getHostAddress() != null)) {
								String ipAddress = address.getHostAddress();
								if(isPublicIp){
									if (!ipAddress.equals("127.0.0.1") && !ipAddress.startsWith("10")  && !ipAddress.startsWith("0")) {
										if (validator.isValidInet4Address(ipAddress)) {
											return ipAddress;
										}
									}
								} else {
									if (!ipAddress.equals("127.0.0.1") && ipAddress.startsWith("10") ) {
										if (validator.isValidInet4Address(ipAddress)) {
											return ipAddress;
										}
									}
								}
								if (validator.isValid(ipAddress)) {
									candidate = ipAddress;
								}
							}
						}
					}
				}
			} catch (SocketException localSocketException) {}
			return candidate;
		}

	   
	   /**
	    * 如果是10开头的IP，统统默认为阿里云的机器
	    * @return
	    */
	   public static boolean isAliyunHost() {
		  String  clientIp = getLocalHostIP(false).trim();
//		  System.out.println("clientIp: " + clientIp);
		  if(Tools.isEmpty(clientIp)) throw new NullPointerException("getLocalHostIP Fail: Ip is Empty!");
//		  if (clientIp.startsWith("127.0") || clientIp.startsWith("192.168")  || "10.163.242.86".equals(clientIp) || "121.42.13.238".equals(clientIp)) {
		  return  clientIp.startsWith("10") ? true : false;
	   }
}
